package com.crawler.search.utils;

import cn.hutool.core.exceptions.ExceptionUtil;
import com.alibaba.fastjson.JSONObject;
import com.crawler.search.constant.EsIndexConstant;
import com.crawler.search.index.suffix.DefaultSuffixer;
import com.crawler.search.index.suffix.Suffixer;
import com.google.common.base.Objects;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
import org.elasticsearch.action.admin.indices.close.CloseIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.GetAliasesResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.*;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.common.xcontent.XContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.Map;
import java.util.Set;

/**
 * @author zhangyonghui
 * @Class Name ESIndexUtils
 * @Create In 下午1:36:12
 */
@Component
public class ESIndexUtils {

    @Autowired
    public RestHighLevelClient client;

    private static Logger logger = LoggerFactory.getLogger(ESIndexUtils.class);

    public static Set<String> indices = Collections.synchronizedSet(Sets.newHashSet());//存储所有的索引别名

    private static String VERSION = EsIndexConstant.VERSION;

    /**
     * 根据json数据设置索引
     *
     * @param index
     * @param mapping
     * @param suffixer 索引别名
     */
    public boolean applyMapping(String index, String mapping, Suffixer suffixer) {
        boolean acknowledged = false;
        try {
            String mappingJson = FileUtils.readFile(mapping);
            logger.info("applyMappingFile:" + mappingJson);
            JSONObject object = JSONObject.parseObject(mappingJson);
            JSONObject totalMapping = object.getJSONObject(EsIndexConstant.MAPPINGS);
            //创建索引，将索引名称例如cn_v1 重命名为cn
            CreateIndexRequest request = new CreateIndexRequest(index);//创建索引
            request.mapping(totalMapping.toJSONString(), XContentType.JSON);
            //设置索引别名
            if (!(suffixer instanceof DefaultSuffixer)) {
                String aliasIndex = suffixer.getAliasIndex(index);
                if (!Objects.equal(aliasIndex, index)) {
                    request.alias(new Alias(aliasIndex));
                }
            }
            if (object.containsKey(EsIndexConstant.SETTINGS)) {
                JSONObject settingObj = object.getJSONObject(EsIndexConstant.SETTINGS);
                request.settings(settingObj.toJSONString(), XContentType.JSON);
            }
            //同步执行
            CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
            //指示是否所有节点都已确认请求
            acknowledged = createIndexResponse.isAcknowledged();
            logger.info("create index: {} ", index, acknowledged ? "success" : "fail");
        } catch (Exception e) {
            logger.error("create index: {} fail，msg :{} ", index, ExceptionUtil.getRootCause(e));
        }
        return acknowledged;
    }


    /**
     * 根据json数据更新mapping
     *
     * @param index
     * @param mapping
     */
    public boolean updateMapping(String index, String mapping) {
        boolean isUpdate = false;
        try {
            JSONObject object = JSONObject.parseObject(mapping);
            JSONObject totalMapping = object.getJSONObject("mappings");
            PutMappingRequest putMappingRequest = new PutMappingRequest(index);
            for (Object o : totalMapping.keySet()) {
                String key = o.toString();
                putMappingRequest.source(totalMapping.getString(key), XContentType.JSON);
            }

            boolean acknowledged = client.indices()
                    .putMapping(putMappingRequest, RequestOptions.DEFAULT)
                    .isAcknowledged();

            logger.info("update {}  mapping {} ", index, acknowledged ? "success" : "fail");
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return isUpdate;
    }


    /**
     * 判断索引别名是否存在，并创建相应的索引别名;
     * 去除原有索引的多个指向
     *
     * @param index
     * @return
     */
    public boolean checkIndexAlias(String index) {
        if (index.endsWith(VERSION)) {
            return true;
        }
        try {
            Map<String, Set<AliasMetaData>> aliases = client.indices()
                    .getAlias(new GetAliasesRequest(index), RequestOptions.DEFAULT)
                    .getAliases();
            for (Map.Entry<String, Set<AliasMetaData>> entry : aliases.entrySet()) {
                //只保留index_v1索引指向index，其余index_n指向删除
                String key = entry.getKey();
                if (!key.equals(index + VERSION)) {
                    for (AliasMetaData meta : entry.getValue()) {
                        IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest()
                                .addAliasAction(IndicesAliasesRequest.AliasActions.remove()
                                        .index(key)
                                        .alias(meta.getAlias()));

                        boolean isAcknowledged = client.indices()
                                .updateAliases(indicesAliasesRequest, RequestOptions.DEFAULT)
                                .isAcknowledged();
                        logger.info("delete index alais: " + meta.getAlias() + " index: " + key + " delete:" + (isAcknowledged ? "success" : "fail"));
                    }
                }
            }
            return true;
        } catch (Exception e) {
            logger.error("delete index alais fail ,error :{} ", index, ExceptionUtil.getMessage(e));
        }
        return false;
    }


    /**
     * 获取ES中存储的索引名称
     *
     * @return
     */
    public Set<String> getAllIndices() {
        try {
            if (!CollectionUtils.isEmpty(indices)) {
                return indices;
            }
            GetAliasesRequest request = new GetAliasesRequest();
            GetAliasesResponse getAliasesResponse = client.indices().getAlias(request, RequestOptions.DEFAULT);
            Map<String, Set<AliasMetaData>> map = getAliasesResponse.getAliases();
            Set<String> keys = map.keySet();
            for (String key : keys) {
                indices.add(key);
            }
        } catch (Exception e) {
            logger.error("get all index fail ,error :{} ", ExceptionUtil.getMessage(e));
        }
        return ESIndexUtils.indices;
    }


    /**
     * 删除索引
     *
     * @param index
     */
    public boolean deleteIndex(String index) {
        boolean isDelete = false;
        try {
            isDelete = client
                    .indices()
                    .delete(new DeleteIndexRequest(index), RequestOptions.DEFAULT)
                    .isAcknowledged();
            logger.info("delete index {} ", index, isDelete ? "success" : "fail");
        } catch (Exception e) {
            logger.error("delete index fail ,error :{} ", ExceptionUtil.getMessage(e));
        }
        return isDelete;
    }

    /**
     * 判断索引是否存在
     *
     * @param index
     * @return
     */
    public boolean existsIndex(String index) {
        boolean flag = false;
        try {
            flag = client.indices().exists(new GetIndexRequest(index), RequestOptions.DEFAULT);
        } catch (Exception e) {
            logger.error("exists index error :{} ", ExceptionUtil.getMessage(e));
        }
        return flag;
    }

    /**
     * 关闭索引
     *
     * @param indexName
     * @return
     */
    public boolean closeIndex(String indexName) {
        boolean flag = false;
        try {
            if (existsIndex(indexName)) {
                flag = client
                        .indices()
                        .close(new CloseIndexRequest(indexName.toLowerCase()), RequestOptions.DEFAULT)
                        .isAcknowledged();
            } else {
                logger.info("index {} not exist  ", indexName);
            }
        } catch (Exception e) {
            logger.error("close index error :{} ", ExceptionUtil.getMessage(e));
        }
        return flag;
    }

    /**
     * 打开索引
     *
     * @param indexName
     * @return
     */
    public boolean openIndex(String indexName) {
        boolean flag = false;
        try {
            if (existsIndex(indexName)) {
                flag = client
                        .indices()
                        .open(new OpenIndexRequest(indexName.toLowerCase()), RequestOptions.DEFAULT)
                        .isAcknowledged();
            } else {
                logger.info("index {} not exist  ", indexName);
            }
        } catch (Exception e) {
            logger.error("open index error :{} ", ExceptionUtil.getMessage(e));
        }
        return flag;
    }


    /**
     * 插入索引
     * @param index
     * @param path
     */
    public  void checkAndCreateIndex(String index, String path,Suffixer suffixer) {
        if (!existsIndex(index)) {
            try {
                this.applyMapping(index,path,suffixer);
            } catch (Exception e) {
                logger.info(e.getMessage());
            }
        }
    }
}
